//
//  MacrosSettingsModule.m
//  Cantrip
//
//  Created by James Callender on Tue Jun 08 2004.
//  Copyright (c) 2004 James Callender. All rights reserved.
//

#import "MacrosSettingsModule.h"


@implementation MacrosSettingsModule

- (id)init
{
    if (self = [super init]) {
        [NSBundle loadNibNamed:@"MacrosSettingsView" owner:self];
    }
    
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

- (void)awakeFromNib
{
    NSTableColumn *tableColumn = nil;
    CantripImageAndTextCell *imageAndTextCell = nil;
        
    tableColumn = [oTableView tableColumnWithIdentifier:@"script"];
    imageAndTextCell = [[[CantripImageAndTextCell alloc] init] autorelease];
    [imageAndTextCell setImage:[NSImage imageNamed:@"ScriptIconSmall"]];
    [imageAndTextCell setEditable: YES];
    [imageAndTextCell setPlaceholderString:@"<none>"];
    [tableColumn setDataCell:imageAndTextCell];
    [oTableView setDataSource:self];
    [oTableView setVerticalMotionCanBeginDrag:NO];
    [oTableView registerForDraggedTypes:[NSArray arrayWithObjects:@"MacrosNodePBoardType", nil]];
    
}

- (NSString *)settingsDisplayName
{
    return @"Macros";
}

- (NSImage *)settingsDisplayIcon
{
    return [NSImage imageNamed:@"Macros.tiff"];
}

- (NSView *)settingsView
{
    return oSettingsView;
}

- (NSSize)size
{
    return NSMakeSize(618, 336);
}

- (id)world 
{
    return currentWorld;
}

- (void)updateForWorld:(id)world
{
    if (world != currentWorld) {
		[self willChangeValueForKey:@"world"];
		currentWorld = world;
		[self didChangeValueForKey:@"world"];
		[oTableView reloadData];
		if ([oEditorPanel isSheet]) {       // We could either do this, or just set a temporary value, 
			[self cancelEditorAction:nil];  // and update after the model is done
		}
    }
}

- (NSDictionary *)defaultWorldProperties
{
    return [NSDictionary dictionaryWithObjectsAndKeys:
	[NSMutableArray arrayWithObjects:
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:91], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP8", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"north", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:88], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP6", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"east", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:84], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP2", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"south", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:86], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP4", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"west", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:89], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP7", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"northwest", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:92], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP9", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"northeast", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:85], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP3", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"southeast", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:83], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP1", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"southwest", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:69], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP+", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"up", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:78], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP-", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"down", MacroScriptKey, nil],
	    [NSMutableDictionary dictionaryWithObjectsAndKeys:
		[NSNumber numberWithInt:87], MacroKeyCodeKey,
		[NSNumber numberWithInt:NSNumericPadKeyMask], MacroModifierFlagsKey,
		[NSNumber numberWithBool:YES], MacroEnabledKey,
		@"KP5", MacroKeyStringKey,
		@"-", MacroModifierStringKey,
		@"look", MacroScriptKey, nil],
	    nil], @"macros",
	nil];
}

- (NSString *)modifierStringForEvent:(NSEvent *)event
{
    NSMutableString *modifierString = [NSMutableString stringWithString:@""];
    int shift = 8679, cmnd = 8984, alt = 8997;
    static NSString *shiftKeyString = nil;
    static NSString *cmndKeyString = nil;
    static NSString *altKeyString = nil;
    
    if (shiftKeyString == nil) {
	shiftKeyString = [[NSString alloc] initWithData:
	    [NSData dataWithBytes:&(shift) length:sizeof(int)]
					       encoding:NSUnicodeStringEncoding];
	cmndKeyString = [[NSString alloc] initWithData:
	    [NSData dataWithBytes:&(cmnd) length:sizeof(int)]
					      encoding:NSUnicodeStringEncoding];
	altKeyString = [[NSString alloc] initWithData:
	    [NSData dataWithBytes:&(alt) length:sizeof(int)]
					     encoding:NSUnicodeStringEncoding];
    }
    
    int modifierFlags = [event modifierFlags];
    
    if (modifierFlags & NSShiftKeyMask) [modifierString appendString:shiftKeyString];
    if (modifierFlags & NSAlternateKeyMask) [modifierString appendString:altKeyString];
    if (modifierFlags & NSCommandKeyMask) [modifierString appendString:cmndKeyString];
    if (modifierFlags & NSControlKeyMask) [modifierString appendString:@"^"];
    
    if ([modifierString length] == 0) return @"-";
    else return modifierString;
}

- (NSString *)keyStringForEvent:(NSEvent *)event
{
    if ([event modifierFlags] & NSNumericPadKeyMask) {
	switch ([event keyCode]) {
	    case 126: return @"<up>"; break;
	    case 124: return @"<down>"; break;
	    case 123: return @"<left>"; break;
	    case 125: return @"<right>"; break;
	    case 76: return @"<enter>"; break;
	    default: return [NSString stringWithFormat:@"KP%@", [event charactersIgnoringModifiers]];
	}
    } else if ([event modifierFlags] & NSFunctionKeyMask) {
	switch ([event keyCode]) {
	    case 122: return @"F1"; break;
	    case 120: return @"F2"; break;
	    case 99: return @"F3"; break;
	    case 118: return @"F4"; break;
	    case 96: return @"F5"; break;
	    case 97: return @"F6"; break;
	    case 98: return @"F7"; break;
	    case 100: return @"F8"; break;
	    case 101: return @"F9"; break;
	    case 109: return @"F10"; break;
	    case 103: return @"F11"; break;
	    case 111: return @"F12"; break;
	    default: return @"F?";
	} 
    } else { 
	switch ([event keyCode]) {
	    case 49: return @"<space>";
	    case 48: return @"<tab>"; break;
	    case 36: return @"<return>"; break;
	    case 53: return @"<esc>"; break;
	    case 51: return @"<delete>"; break;
	    default: return [NSString stringWithFormat:@"%@", [event charactersIgnoringModifiers]];
	}
    }
    
}

- (id)menuForTableColumn:(NSTableColumn *)column row:(int)row
{
    NSDictionary *macro = [[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:row];
    NSMenu *menu;
    NSMenuItem *item;

    menu = [[NSMenu alloc] init];
    
    item = [[NSMenuItem alloc] initWithTitle:@"Edit..." action:@selector(editMacroAction:) keyEquivalent:@""];
    [item setRepresentedObject:macro];
    [item setTarget:self];
    [menu addItem:[item autorelease]];
    
    [menu addItem:[NSMenuItem separatorItem]];
    
    item = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(removeMacroAction:) keyEquivalent:@""];
    [item setRepresentedObject:macro];
    [item setTarget:self];
    [menu addItem:[item autorelease]];
    
    return [menu autorelease];
    
}

- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSDictionary *macro = [[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:row];
    if ([[tableColumn identifier] isEqualToString:@"script"]) {
	if ([[macro objectForKey:MacroScriptKey] isEqualToString:@""]) {
	    [cell setPlaceholderString:@"<none>"];
	}
    }
}

- (int)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [[currentWorld valueForKeyPath:@"data.macros"] count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSDictionary *macro = [[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:row];
    if ([[tableColumn identifier] isEqualToString:@"keyString"]) {
	if (![[macro objectForKey:MacroModifierStringKey] isEqualToString:@"-"]) {
	    return [NSString stringWithFormat:@"%@ + %@", [macro objectForKey:MacroModifierStringKey], [macro objectForKey:MacroKeyStringKey]];
	}
    }
    return [macro objectForKey:[tableColumn identifier]];
}


- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
{
    NSMutableDictionary *macro = [[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:row];

    if ([[tableColumn identifier] isEqualToString:@"script"]) {
	if (![[macro objectForKey:MacroScriptKey] isEqual:object]) {
	    [[[currentWorld undoManager] prepareWithInvocationTarget:self] undoChangeValue:[macro objectForKey:MacroScriptKey] ofKey:MacroScriptKey forMacro:macro];
	    [macro setObject:object forKey:MacroScriptKey];
	    [currentWorld updateChangeCount:NSChangeDone];
	}
    } else if ([[tableColumn identifier] isEqualToString:@"enabled"]) {
	if (![[macro objectForKey:MacroEnabledKey] isEqual:object]) {
	    [[[currentWorld undoManager] prepareWithInvocationTarget:self] undoChangeValue:[macro objectForKey:MacroEnabledKey] ofKey:MacroEnabledKey forMacro:macro];
	    [macro setObject:object forKey:MacroEnabledKey];
	    [currentWorld updateChangeCount:NSChangeDone];
	}
    }
}

- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard
{
    draggedRows = rows;
    [pboard declareTypes:[NSArray arrayWithObjects: @"MacrosNodePBoardType", nil] owner:self];
    [pboard setString:@"<Cantrip Macro>" forType:@"MacrosNodePBoardType"]; 
    
    return YES;
}

- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op;
{
    NSPasteboard * pboard = [info draggingPasteboard];
    if (op == NSTableViewDropOn) return NSDragOperationNone;

    if ([[pboard types] containsObject:@"MacrosNodePBoardType"]) return NSDragOperationGeneric;
    
    return NSDragOperationNone;
}

- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
{
    NSMutableArray *draggedMacros = [NSMutableArray array];
    NSEnumerator *e; id n;
    int newRow = row;
    
    //[[currentWorld undoManager] beginUndoGrouping];
    e = [draggedRows objectEnumerator];
    while (n = [e nextObject]) {
	int itemRow = [n intValue];
	[draggedMacros addObject:[[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:itemRow]];
	
	if (itemRow < row) newRow--;
    }
    //[[currentWorld undoManager] endUndoGrouping];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
		undoMoveMacros:draggedMacros
		     fromIndex:[[draggedRows objectAtIndex:0] intValue]];
    
    [[currentWorld valueForKeyPath:@"data.macros"] removeObjectsInArray:draggedMacros];
    [[currentWorld valueForKeyPath:@"data.macros"] insertObjectsFromArray:draggedMacros atIndex:newRow];
    [currentWorld updateChangeCount:NSChangeDone];
    [tv selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(newRow, [draggedMacros count])] byExtendingSelection:NO];
    [tv reloadData];
    
    return YES;
}

#pragma mark Interface Actions

- (void)undoMoveMacros:(NSArray *)macros fromIndex:(int)index
{
    int currentIndex = [[currentWorld valueForKeyPath:@"data.macros"] indexOfObjectIdenticalTo:[macros objectAtIndex:0]];
    //if (currentIndex < index) index--;
    
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] undoMoveMacros:macros fromIndex:currentIndex];
    
    [[currentWorld valueForKeyPath:@"data.macros"] removeObjectsInArray:macros];
    [[currentWorld valueForKeyPath:@"data.macros"] insertObjectsFromArray:macros atIndex:index];
    [oTableView selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, [macros count])] byExtendingSelection:NO];
    
    if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeUndone];
    else if ([[currentWorld undoManager] isRedoing]) [currentWorld updateChangeCount:NSChangeDone];
    [oTableView reloadData];
}

- (void)undoChangeValue:(id)value ofKey:(NSString *)key forMacro:(id)macro
{
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] undoChangeValue:[macro objectForKey:key] ofKey:key forMacro:macro];
    [macro setObject:value forKey:key];
    if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeUndone];
    else if ([[currentWorld undoManager] isRedoing]) [currentWorld updateChangeCount:NSChangeDone];
    [oTableView reloadData];
}

- (IBAction)saveEditorAction:(id)sender
{
    [[currentWorld undoManager] beginUndoGrouping];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroScriptKey] ofKey:MacroScriptKey forMacro:currentlyEditingMacro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroEnabledKey] ofKey:MacroEnabledKey forMacro:currentlyEditingMacro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroModifierStringKey] ofKey:MacroModifierStringKey forMacro:currentlyEditingMacro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroKeyStringKey] ofKey:MacroKeyStringKey forMacro:currentlyEditingMacro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroKeyCodeKey] ofKey:MacroKeyCodeKey forMacro:currentlyEditingMacro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] 
	undoChangeValue:[currentlyEditingMacro objectForKey:MacroModifierFlagsKey] ofKey:MacroModifierFlagsKey forMacro:currentlyEditingMacro];
    [[currentWorld undoManager] endUndoGrouping];
    
    [currentlyEditingMacro setObject:[oScriptArea string] forKey:MacroScriptKey];
    [currentlyEditingMacro setObject:[NSNumber numberWithBool:([oEnabledButton state] == NSOnState)] forKey:MacroEnabledKey];
    [currentlyEditingMacro setObject:[oModifierField stringValue] forKey:MacroModifierStringKey];
    [currentlyEditingMacro setObject:[oKeyField stringValue] forKey:MacroKeyStringKey];
    [currentlyEditingMacro setObject:[NSNumber numberWithInt:tempKeyCode] forKey:MacroKeyCodeKey];
    [currentlyEditingMacro setObject:[NSNumber numberWithInt:tempModifierFlags] forKey:MacroModifierFlagsKey];

    [NSApp endSheet:oEditorPanel];
    [oEditorPanel orderOut:nil];
    
    [currentWorld updateChangeCount:NSChangeDone];
}

- (IBAction)cancelEditorAction:(id)sender
{
    [NSApp endSheet:oEditorPanel];
    [oEditorPanel orderOut:nil];
}

- (IBAction)setKeyButtonAction:(id)sender
{
    NSEvent *theEvent;
    
    [oPromptPanel center];
    [oPromptPanel makeKeyAndOrderFront:self];
    
    theEvent = [NSApp nextEventMatchingMask:NSKeyDownMask | NSLeftMouseDownMask untilDate:[[NSDate alloc] initWithTimeIntervalSinceNow:10] inMode:NSDefaultRunLoopMode dequeue:YES];
    
    [oPromptPanel orderOut:self];
    
    if (theEvent == nil) { NSBeep(); return; }
    if ([theEvent type] == NSLeftMouseDown) { NSBeep(); return; }
    
    tempKeyCode = [theEvent keyCode];
    tempModifierFlags = [theEvent modifierFlags];
    
    [oModifierField setStringValue:[self modifierStringForEvent:theEvent]];
    [oKeyField setStringValue:[self keyStringForEvent:theEvent]];
}

- (IBAction)editMacroAction:(id)sender
{
    currentlyEditingMacro = [sender representedObject];
    
    [oScriptArea setString:[currentlyEditingMacro objectForKey:MacroScriptKey]];
    [oEnabledButton setState:[[currentlyEditingMacro objectForKey:MacroEnabledKey] boolValue]];
    [oModifierField setStringValue:[currentlyEditingMacro objectForKey:MacroModifierStringKey]];
    [oKeyField setStringValue:[currentlyEditingMacro objectForKey:MacroKeyStringKey]];
    
    tempKeyCode = [[currentlyEditingMacro objectForKey:MacroKeyCodeKey] intValue];
    tempModifierFlags = [[currentlyEditingMacro objectForKey:MacroModifierFlagsKey] intValue];
    
    [NSApp  beginSheet:oEditorPanel modalForWindow:[oTableView window]
	 modalDelegate:self didEndSelector:NULL contextInfo:nil];
}

- (IBAction)removeMacroAction:(id)sender
{
    //[[currentWorld valueForKeyPath:@"data.macros"] removeObjectIdenticalTo:[sender representedObject]];
    [self removeMacro:[sender representedObject]];
    [oTableView reloadData];
}

- (IBAction)removeAction:(id)sender
{
    NSIndexSet *items = [oTableView selectedRowIndexes];
    NSMutableArray *macrosToDelete = [NSMutableArray array];
    NSEnumerator *e; id n;
    int i = [items firstIndex];
    
    if (![items count]) return;
    
    for (;i <= [items lastIndex]; i++) {
	[macrosToDelete addObject:[[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:i]];
	//[self removeMacro:[[currentWorld valueForKeyPath:@"data.macros"] objectAtIndex:i]];
    }    
    
    e = [macrosToDelete objectEnumerator];
    [[currentWorld undoManager] beginUndoGrouping];
    while (n = [e nextObject]) {
	[self removeMacro:n];
    }
    [[currentWorld undoManager] endUndoGrouping];

    [oTableView deselectAll:nil];
    [oTableView reloadData];
}

- (IBAction)addAction:(id)sender
{
    NSEvent *theEvent;
    NSMutableDictionary *newMacro;
    
    [oPromptPanel center];
    [oPromptPanel makeKeyAndOrderFront:self];
    
    theEvent = [NSApp nextEventMatchingMask:NSKeyDownMask | NSLeftMouseDownMask untilDate:[[NSDate alloc] initWithTimeIntervalSinceNow:10] inMode:NSDefaultRunLoopMode dequeue:YES];
    
    [oPromptPanel orderOut:self];
    
    if (theEvent == nil) { NSBeep(); return; }
    if ([theEvent type] == NSLeftMouseDown) { NSBeep(); return; }
    
    newMacro = [[NSMutableDictionary alloc] init];
    [newMacro setObject:[NSNumber numberWithInt:[theEvent keyCode]] forKey:MacroKeyCodeKey];
    [newMacro setObject:[NSNumber numberWithInt:[theEvent modifierFlags]] forKey:MacroModifierFlagsKey];
    [newMacro setObject:[NSNumber numberWithBool:YES] forKey:MacroEnabledKey];
    [newMacro setObject:@"" forKey:MacroScriptKey];
    
    [newMacro setObject:[self keyStringForEvent:theEvent] forKey:MacroKeyStringKey];
    [newMacro setObject:[self modifierStringForEvent:theEvent] forKey:MacroModifierStringKey];
    
    /*
    [currentWorld willChangeValueForKey:@"data"];
    [[currentWorld valueForKeyPath:@"data.macros"] addObject:[newMacro autorelease]];
    [currentWorld didChangeValueForKey:@"data"];
    //[[currentWorld undoManager] registerUndoWithTarget:[currentWorld valueForKeyPath:@"data.macros"] 
    //					      selector:@selector(removeObject:) object:newMacro];
     
    [[currentWorld undoManager] beginUndoGrouping];
    [[currentWorld undoManager] registerUndoWithTarget:self selector:@selector(removeMacro:) object:newMacro];
    //[[currentWorld undoManager] setActionName:@"Add Macro"];
    [[currentWorld undoManager] endUndoGrouping];
    
    [currentWorld updateChangeCount:NSChangeDone];
    [oTableView reloadData];*/
    [self addMacro:newMacro];
}

#pragma mark Accessors

- (void)addMacro:(id)macro
{
    [self addMacro:macro atIndex:-1];
}

- (void)addMacro:(id)macro atIndex:(int)index
{
    NSMutableArray *macros = [currentWorld valueForKeyPath:@"data.macros"];
    //[[currentWorld undoManager] beginUndoGrouping];
    [[currentWorld undoManager] registerUndoWithTarget:self selector:@selector(removeMacro:) object:macro];
    //[[currentWorld undoManager] setActionName:@"Add Macro"];
    //[[currentWorld undoManager] endUndoGrouping];
    
    if (index >= 0 && index < [macros count]) [macros insertObject:macro atIndex:index];
    else [macros addObject:macro];
    
    if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeUndone];
    else if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeDone];
    [oTableView reloadData];
}

- (void)removeMacro:(id)macro
{
    NSMutableArray *macros = [currentWorld valueForKeyPath:@"data.macros"];
    //[[currentWorld undoManager] beginUndoGrouping];
    //[[currentWorld undoManager] registerUndoWithTarget:self selector:@selector(addMacro:) object:macro];
    [[[currentWorld undoManager] prepareWithInvocationTarget:self] addMacro:macro atIndex:[macros indexOfObjectIdenticalTo:macro]];
    //[[currentWorld undoManager] setActionName:@"Remove Macro"];
    //[[currentWorld undoManager] endUndoGrouping];
    
    [macros removeObject:macro];
    if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeUndone];
    else if ([[currentWorld undoManager] isUndoing]) [currentWorld updateChangeCount:NSChangeDone];
    [oTableView reloadData];
}

@end
